/********************************************************************************
 * Copyright (c) 2011-2017 Red Hat Inc. and/or its affiliates and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 1.0 which is available at
 * http://www.eclipse.org/legal/epl-v10.html.
 *
 * SPDX-License-Identifier: EPL-1.0
 ********************************************************************************/
package org.eclipse.ceylon.ide.eclipse.code.correct;

import static org.eclipse.ceylon.ide.eclipse.code.correct.AssignToAssertExistsProposal.addAssignToAssertExistsProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.AssignToAssertIsProposal.addAssignToAssertIsProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.AssignToAssertNonemptyProposal.addAssignToAssertNonemptyProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.AssignToForProposal.addAssignToForProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.AssignToIfExistsProposal.addAssignToIfExistsProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.AssignToIfIsProposal.addAssignToIfIsProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.AssignToIfNonemptyProposal.addAssignToIfNonemptyProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.AssignToTryProposal.addAssignToTryProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.ConvertFunctionToGetterProposal.addConvertFunctionToGetterProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.ConvertGetterToFunctionProposal.addConvertGetterToFunctionProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.DestructureProposal.addDestructureProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.MoveDirProposal.addMoveDirProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.PrintProposal.addPrintProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.RemoveAliasProposal.addRemoveAliasProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.RenameAliasProposal.addRenameAliasProposal;
import static org.eclipse.ceylon.ide.eclipse.code.correct.RenameVersionProposal.addRenameVersionProposals;
import static org.eclipse.ceylon.ide.eclipse.code.correct.UseAliasProposal.addUseAliasProposal;
import static org.eclipse.ceylon.ide.eclipse.core.builder.CeylonBuilder.MODULE_DEPENDENCY_PROBLEM_MARKER_ID;
import static org.eclipse.ceylon.ide.eclipse.core.builder.CeylonBuilder.PROBLEM_MARKER_ID;
import static org.eclipse.ceylon.ide.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker;
import static org.eclipse.ceylon.ide.eclipse.core.builder.MarkerCreator.ERROR_CODE_KEY;
import static org.eclipse.ceylon.ide.eclipse.java2ceylon.Java2CeylonProxies.correctJ2C;
import static org.eclipse.ceylon.ide.eclipse.util.AnnotationUtils.getAnnotationsForLine;
import static org.eclipse.ceylon.ide.eclipse.util.EditorUtil.getCurrentEditor;
import static org.eclipse.ceylon.ide.eclipse.util.Nodes.findArgument;
import static org.eclipse.ceylon.ide.eclipse.util.Nodes.findDeclaration;
import static org.eclipse.ceylon.ide.eclipse.util.Nodes.findImport;
import static org.eclipse.ceylon.ide.eclipse.util.Nodes.findNode;
import static org.eclipse.ceylon.ide.eclipse.util.Nodes.findOperator;
import static org.eclipse.ceylon.ide.eclipse.util.Nodes.findStatement;
import static org.eclipse.ui.PlatformUI.getWorkbench;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
import org.eclipse.jface.text.quickassist.QuickAssistAssistant;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.MarkerAnnotation;

import org.eclipse.ceylon.compiler.typechecker.TypeChecker;
import org.eclipse.ceylon.compiler.typechecker.analyzer.UsageWarning;
import org.eclipse.ceylon.compiler.typechecker.context.PhasedUnit;
import org.eclipse.ceylon.compiler.typechecker.tree.Node;
import org.eclipse.ceylon.compiler.typechecker.tree.Tree;
import org.eclipse.ceylon.ide.eclipse.code.editor.CeylonAnnotation;
import org.eclipse.ceylon.ide.eclipse.code.editor.CeylonEditor;
import org.eclipse.ceylon.ide.eclipse.code.parse.CeylonParseController;
import org.eclipse.ceylon.ide.eclipse.core.builder.MarkerCreator;
import org.eclipse.ceylon.ide.eclipse.util.EditorUtil;
import org.eclipse.ceylon.ide.eclipse.util.MarkerUtils;

public class CeylonCorrectionProcessor extends QuickAssistAssistant 
        implements IQuickAssistProcessor {
    
    private static final ProblemLocation[] NO_PROBLEM_LOCATIONS = new ProblemLocation[0];
    private static final ICompletionProposal[] NO_PROPOSALS = new ICompletionProposal[0];

    CeylonEditor editor; //may only be used for quick assists!!!
    private Tree.CompilationUnit model;
    private IFile file; //may only be used for markers!
    
    public CeylonCorrectionProcessor(CeylonEditor editor) {
        this.editor = editor;
        setQuickAssistProcessor(this);        
    }

    public CeylonCorrectionProcessor(IMarker marker) {
        IFileEditorInput input = MarkerUtils.getInput(marker);
        if (input!=null) {
            file = input.getFile();
            IProject project = file.getProject();
            IJavaProject javaProject = JavaCore.create(project);
            TypeChecker tc = getProjectTypeChecker(project);
            if (tc!=null) {
                try {
                    for (IPackageFragmentRoot pfr:
                            javaProject.getPackageFragmentRoots()) {
                        if (pfr.getPath()
                                .isPrefixOf(file.getFullPath())) {
                            IPath relPath =
                                    file.getFullPath()
                                        .makeRelativeTo(
                                                pfr.getPath());
                            PhasedUnit pu =
                                    tc.getPhasedUnitFromRelativePath(
                                            relPath.toString());
                            model = pu.getCompilationUnit();
                        }
                    }
                } 
                catch (JavaModelException e) {
                    e.printStackTrace();
                }
            }
        }
        setQuickAssistProcessor(this);
    }
    
    private IFile getFile() {
        if (editor!=null) {
            IEditorInput ei = editor.getEditorInput();
            if (ei instanceof FileEditorInput) {
                FileEditorInput input =
                        (FileEditorInput) ei;
                if (input!=null) {
                    return input.getFile();
                }
            }
        }
        return file;
    }
    
    private Tree.CompilationUnit getRootNode() {
        if (editor!=null) {
            Tree.CompilationUnit upToDateRootNode =
                    editor.getParseController()
                        .getTypecheckedRootNode();
            if (upToDateRootNode != null) {
                return upToDateRootNode;
            }
        }

        if (model!=null) {
            return (Tree.CompilationUnit) model;
        }

        return null;
    }
    
    @Override
    public String getErrorMessage() {
        return null;
    }
    
    private void collectProposals(
            IQuickAssistInvocationContext context,
            IAnnotationModel model,
            Collection<Annotation> annotations,
            boolean addQuickFixes, boolean addQuickAssists,
            Collection<ICompletionProposal> proposals) {
        ArrayList<ProblemLocation> problems = 
                new ArrayList<ProblemLocation>();
        // collect problem locations and corrections from marker annotations
        for (Annotation curr: annotations) {
            if (curr instanceof CeylonAnnotation) {
                CeylonAnnotation ca = (CeylonAnnotation) curr;
                ProblemLocation problemLocation = 
                        getProblemLocation(ca, model);
                if (problemLocation != null) {
                    problems.add(problemLocation);
                }
            }
            else if (curr instanceof MarkerAnnotation) {
                MarkerAnnotation ma = (MarkerAnnotation) curr;
                ProblemLocation problemLocation =
                        getProblemLocation(ma, model);
                if (problemLocation != null) {
                    problems.add(problemLocation);
                }
            }
        }

        ProblemLocation[] problemLocations =
                problems.toArray(NO_PROBLEM_LOCATIONS);
        Arrays.sort(problemLocations);
        if (addQuickFixes) {
            collectCorrections(context, problemLocations, proposals);
        }
        if (addQuickAssists) {
            collectAssists(context, problemLocations, proposals);
        }
        if (addQuickFixes) {
            addSuppressWarningsProposals(context, model, annotations, proposals);
        }
    }

    public void addSuppressWarningsProposals(
            IQuickAssistInvocationContext context,
            IAnnotationModel model,
            Collection<Annotation> annotations,
            Collection<ICompletionProposal> proposals) {
        for (Annotation curr: annotations) {
            if (curr instanceof CeylonAnnotation) {
                CeylonAnnotation ca = (CeylonAnnotation) curr;
                if (ca.getSeverity()==IMarker.SEVERITY_WARNING) {
                    ProblemLocation problemLocation = 
                            getProblemLocation(ca, model);
                    if (problemLocation != null) {
                        collectWarningProposals(ca, context,
                                problemLocation, proposals);
                        break;
                    }
                }
            }
        }
    }

    private static ProblemLocation getProblemLocation(
            CeylonAnnotation annotation,
            IAnnotationModel model) {
        int problemId = annotation.getId();
        if (problemId != -1) {
            Position pos = model.getPosition(annotation);
            if (pos != null) {
                return new ProblemLocation(
                        pos.getOffset(), pos.getLength(),
                        problemId); // java problems all handled by the quick assist processors
            }
        }
        return null;
    }

    private static ProblemLocation getProblemLocation(
            MarkerAnnotation annotation,
            IAnnotationModel model) {
        Integer problemId = null;
        try {
            problemId = (Integer)
                annotation.getMarker()
                    .getAttribute(MarkerCreator.ERROR_CODE_KEY);
        }
        catch (CoreException e) {
            e.printStackTrace();
        }
        if (problemId != null) {
            Position pos = model.getPosition(annotation);
            if (pos != null) {
                return new ProblemLocation(
                        pos.getOffset(), pos.getLength(),
                        problemId); // java problems all handled by the quick assist processors
            }
        }
        return null;
    }

    private void collectAssists(
            IQuickAssistInvocationContext context,
            ProblemLocation[] locations,
            Collection<ICompletionProposal> proposals) {
        if (proposals.isEmpty()) {
            addProposalsWithProgress(context, editor, proposals);
        }
    }

    @Override
    public ICompletionProposal[] computeQuickAssistProposals(
            IQuickAssistInvocationContext context) {
        ArrayList<ICompletionProposal> proposals =
                new ArrayList<ICompletionProposal>();
        ISourceViewer viewer = context.getSourceViewer();
        List<Annotation> annotations = 
                getAnnotationsForLine(viewer,
                        getLine(context, viewer));
        collectProposals(context, viewer.getAnnotationModel(),
                annotations, true, true, proposals);
        return proposals.toArray(NO_PROPOSALS);
    }

    private void addProposalsWithProgress(
            final IQuickAssistInvocationContext context,
            final ProblemLocation location, final IFile file,
            final Tree.CompilationUnit rootNode,
            final Collection<ICompletionProposal> proposals) {
        class Runnable implements IRunnableWithProgress {
            @Override
            public void run(IProgressMonitor monitor)
                    throws InvocationTargetException,
                           InterruptedException {
                monitor.beginTask("Preparing fix proposals...",
                        IProgressMonitor.UNKNOWN);
                addProposals(context, location, file, rootNode, proposals);
                monitor.done();
            }
        }
        Runnable runnable = new Runnable();
        try {
            getWorkbench()
                .getActiveWorkbenchWindow()
                .run(true, true, runnable);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void addProposalsWithProgress(
            final IQuickAssistInvocationContext context,
            final CeylonEditor editor,
            final Collection<ICompletionProposal> proposals) {
        class Runnable implements IRunnableWithProgress {
            @Override
            public void run(IProgressMonitor monitor)
                    throws InvocationTargetException,
                           InterruptedException {
                monitor.beginTask("Preparing assist proposals...",
                        IProgressMonitor.UNKNOWN);
                addProposals(context, editor, proposals);
                monitor.done();
            }
        }
        Runnable runnable = new Runnable();
        try {
            getWorkbench()
                .getActiveWorkbenchWindow()
                //we have to run this in the UI thread
                .run(false, true, runnable);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    private int getLine(
            IQuickAssistInvocationContext context,
            ISourceViewer viewer) {
        try {
            return viewer.getDocument()
                    .getLineOfOffset(context.getOffset());
        } 
        catch (BadLocationException e) {
            e.printStackTrace();
            return 0;
        }
    }
    
    public void collectCorrections(
            IQuickAssistInvocationContext context,
            ProblemLocation location,
            Collection<ICompletionProposal> proposals) {
        Tree.CompilationUnit rootNode = getRootNode();
        if (rootNode!=null) {
            addProposals(context, location, getFile(), 
                    rootNode, proposals);
        }
    }
    
    private void collectCorrections(
            IQuickAssistInvocationContext context,
            ProblemLocation[] locations,
            Collection<ICompletionProposal> proposals) {
        ISourceViewer viewer = context.getSourceViewer();
        Tree.CompilationUnit rootNode = getRootNode();
        if (rootNode == null) {
            return;
        }
        for (int i=locations.length-1; i>=0; i--) {
            ProblemLocation loc = locations[i];
            if (loc.getOffset()<=viewer.getSelectedRange().x) {
                for (int j=i; j>=0; j--) {
                    ProblemLocation location = locations[j];
                    if (location.getOffset()!=loc.getOffset()) {
                        break;
                    }
                    addProposalsWithProgress(context,
                            location, getFile(),
                            rootNode, proposals);
                }
                if (!proposals.isEmpty()) {
                    viewer.setSelectedRange(loc.getOffset(), 
                            loc.getLength());
                    return;
                }
            }
        }
        for (int i=0; i<locations.length; i++) {
            ProblemLocation loc = locations[i];
            for (int j=i; j<locations.length; j++) {
                ProblemLocation location = locations[j];
                if (location.getOffset()!=loc.getOffset()) break;
                addProposalsWithProgress(context,
                        location, getFile(),
                        rootNode, proposals);
            }
            if (!proposals.isEmpty()) {
                viewer.setSelectedRange(loc.getOffset(), 
                        loc.getLength());
                return;
            }
        }
    }

    public static boolean canFix(IMarker marker)  {
        try {
            String mt = marker.getType();
            if (mt.equals(PROBLEM_MARKER_ID) ||
                mt.equals(MODULE_DEPENDENCY_PROBLEM_MARKER_ID)) {
                int code = marker.getAttribute(ERROR_CODE_KEY, 0);
                return code>0;
            }
            else {
                return false;
            }
        }
        catch (CoreException e) {
            return false;
        }
    }
    
    @Override
    public boolean canFix(Annotation annotation) {
        if (annotation instanceof CeylonAnnotation) {
            CeylonAnnotation ceylonAnnotation =
                    (CeylonAnnotation) annotation;
            return ceylonAnnotation.isFixable();
        }
        else if (annotation instanceof MarkerAnnotation) {
            MarkerAnnotation markerAnnotation =
                    (MarkerAnnotation) annotation;
            return canFix(markerAnnotation.getMarker());
        }
        else {
            return false;
        }
    }

    @Override
    public boolean canAssist(IQuickAssistInvocationContext context) {
        //oops, all this is totally useless, because
        //this method never gets called :-/
        /*Tree.CompilationUnit cu = (CompilationUnit) context.getModel()
                .getAST(new NullMessageHandler(), new NullProgressMonitor());
        return CeylonSourcePositionLocator.findNode(cu, null, context.getOffset(), 
                context.getOffset()+context.getLength()) instanceof Tree.Term;*/
        return true;
    }
    
    CeylonEditor getCurrentCeylonEditor() {
        if (editor != null) {
            return editor;
        }
        IEditorPart editorPart = getCurrentEditor();
        if (editorPart instanceof CeylonEditor) {
            return (CeylonEditor) editorPart;
        }
        return null;
    }
    
    private void addProposals(
            IQuickAssistInvocationContext context,
            ProblemLocation problem, IFile file, 
            Tree.CompilationUnit rootNode,
            Collection<ICompletionProposal> proposals) {
        if (file==null) return;
        IProject project = file.getProject();
        TypeChecker tc = getProjectTypeChecker(project);
        int start = problem.getOffset();
        int end = start + problem.getLength();
        Node node = findNode(rootNode, null, start, end);

        ISourceViewer sourceViewer = context.getSourceViewer();
        correctJ2C().addQuickFixes(problem, rootNode, node, project, proposals,
                getCurrentCeylonEditor(), tc, file, 
                sourceViewer == null ? null : sourceViewer.getDocument());

        switch (problem.getProblemId()) {
//        case 100:
//            addDeclareLocalProposal(rootNode, node, proposals, file, editor);
//            //fall through:
//        case 102:
//            if (tc!=null) {
//                importProposals().addImportProposals(rootNode, node, proposals, file);
//            }
//            addCreateEnumProposal(rootNode, node, problem, proposals, project);
//            addCreationProposals(rootNode, node, problem, proposals, project, file);
//            if (tc!=null) {
//                addChangeReferenceProposals(rootNode, node, problem, proposals, file);
//            }
//            break;
//        case 101:
//            addCreateParameterProposals(rootNode, node, problem, proposals, project);
//            if (tc!=null) {
//                addChangeArgumentReferenceProposals(rootNode, node, problem, proposals, file);
//            }
//            break;
//        case 200:
//            addSpecifyTypeProposal(rootNode, node, proposals, null);
//            break;
//        case 300:
//            addRefineFormalMembersProposal(proposals, node, rootNode, false);
//            addMakeAbstractDecProposal(proposals, project, node);
//            break;
//        case 350:
//            addRefineFormalMembersProposal(proposals, node, rootNode, true);
//            addMakeAbstractDecProposal(proposals, project, node);
//            break;
//        case 310:
//            addMakeAbstractDecProposal(proposals, project, node);
//            break;
//        case 320:
//            addRemoveAnnotationProposal(node, "formal", proposals, project);
//            break;
//        case 400:
//        case 402:
//            addMakeSharedProposal(proposals, project, node);
//            break;
//        case 705:
//            addMakeSharedDecProposal(proposals, project, node);
//            break;
//        case 500:
//        case 510:
//            addMakeDefaultProposal(proposals, project, node);
//            break;
//        case 600:
//            addMakeActualDecProposal(proposals, project, node);
//            break;
//        case 701:
//            addMakeSharedDecProposal(proposals, project, node);
//            addRemoveAnnotationDecProposal(proposals, "actual", project, node);
//            break;
//        case 702:
//            addMakeSharedDecProposal(proposals, project, node);
//            addRemoveAnnotationDecProposal(proposals, "formal", project, node);
//            break;
//        case 703:
//            addMakeSharedDecProposal(proposals, project, node);
//            addRemoveAnnotationDecProposal(proposals, "default", project, node);
//            break;
//        case 710:
//        case 711:
//            addMakeSharedProposal(proposals, project, node);
//            break;
//        case 712:
//            addExportModuleImportProposal(proposals, project, node);
//            break;
//        case 713:
//            addMakeSharedProposalForSupertypes(proposals, project, node);
//            break;
//        case 714:
//            addExportModuleImportProposalForSupertypes(proposals, project, node, rootNode);
//            break;
//        case 800:
//        case 804:
//            addMakeVariableProposal(proposals, project, node);
//            break;
//        case 803:
//            addMakeVariableProposal(proposals, project, node);
//            break;
//        case 801:
//            addMakeVariableDecProposal(proposals, project, rootNode, node);
//            break;
//        case 802:
//            break;
//        case 905:
//            addMakeContainerAbstractProposal(proposals, project, node);
//            break;
//        case 1100:
//            addMakeContainerAbstractProposal(proposals, project, node);
//            addRemoveAnnotationDecProposal(proposals, "formal", project, node);
//            break;
//        case 1101:
//            addRemoveAnnotationDecProposal(proposals, "formal", project, node);
//            //TODO: replace body with ;
//            break;
//        case 1000:
//        case 1001:
//            addEmptyParameterListProposal(file, proposals, node);
//            addParameterListProposal(file, proposals, node, rootNode, false);
//            addConstructorProposal(file, proposals, node, rootNode);
//            addChangeDeclarationProposal(problem, file, proposals, node);
//            break;
//        case 1020:
//            addImportWildcardProposal(file, proposals, node);
//            break;
//        case 1050:
//            addFixAliasProposal(proposals, file, problem);
//            break;
//        case 1200:
//        case 1201:
//            addRemoveAnnotationDecProposal(proposals, "shared", project, node);
//            break;
//        case 1300:
//        case 1301:
//            addMakeRefinedSharedProposal(proposals, project, node);
//            addRemoveAnnotationDecProposal(proposals, "actual", project, node);
//            break;
//        case 1303:
//        case 1313:
//        case 1320:
//            addRemoveAnnotationDecProposal(proposals, "formal", project, node);
//            addRemoveAnnotationDecProposal(proposals, "default", project, node);
//            break;
//        case 1350:
//            addRemoveAnnotationDecProposal(proposals, "default", project, node);
//            addMakeContainerNonfinalProposal(proposals, project, node);
//            break;
//        case 1400:
//        case 1401:
//            addMakeFormalDecProposal(proposals, project, node);
//            break;
//        case 1450:
//        	addMakeFormalDecProposal(proposals, project, node);
//        	addParameterProposals(proposals, file, rootNode, node);
//        	addInitializerProposals(proposals, file, rootNode, node);
//          addParameterListProposal(file, proposals, node, rootNode, false);
//        	addConstructorProposal(file, proposals, node, rootNode);
//        	break;
//        case 1610:
//            addRemoveAnnotationDecProposal(proposals, "shared", project, node);
//            addRemoveAnnotationDecProposal(proposals, "abstract", project, node);
//            break;
//        case 1500:
//        case 1501:
//            addRemoveAnnotationDecProposal(proposals, "variable", project, node);
//            break;
//        case 1600:
//        case 1601:
//            addRemoveAnnotationDecProposal(proposals, "abstract", project, node);
//            break;
//        case 1700:
//            addRemoveAnnotationDecProposal(proposals, "final", project, node);
//            break;
//        case 1800:
//        case 1801:
//            addRemoveAnnotationDecProposal(proposals, "sealed", project, node);
//            break;
//        case 1900:
//            addRemoveAnnotationDecProposal(proposals, "late", project, node);
//            break;
//        case 1950:
//        case 1951:
//            addRemoveAnnotationDecProposal(proposals, "annotation", project, node);
//            break;
//        case 2000:
//            addCreateParameterProposals(rootNode, node, problem, proposals, project);
//            break;
//        case 2100:
//            addAppendMemberReferenceProposals(rootNode, node, problem, proposals, file);
//            addChangeTypeProposals(rootNode, node, problem, proposals, project);
//            addSatisfiesProposals(rootNode, node, proposals, project);
//            break;
//        case 2102:
//            addChangeTypeArgProposals(rootNode, node, problem, proposals, project);
//            addSatisfiesProposals(rootNode, node, proposals, project);
//            break;
//        case 2101:
//            addSpreadToSequenceParameterProposal(rootNode, node, proposals, file);
//            break;
//        case 2500:
//            addTypeParameterProposal(file, rootNode, proposals, node);
//            break;
        case 3000:
            CeylonEditor currentEditor = getCurrentCeylonEditor();
//            addAssignToLocalProposal(currentEditor, rootNode, proposals, node, start);
            addDestructureProposal(currentEditor, rootNode, proposals, node, start);
            addAssignToForProposal(currentEditor, rootNode, proposals, node, start);
            addAssignToIfExistsProposal(currentEditor, rootNode, proposals, node, start);
            addAssignToAssertExistsProposal(currentEditor, rootNode, proposals, node, start);
            addAssignToIfNonemptyProposal(currentEditor, rootNode, proposals, node, start);
            addAssignToAssertNonemptyProposal(currentEditor, rootNode, proposals, node, start);
            addAssignToTryProposal(currentEditor, rootNode, proposals, node, start);
            addAssignToIfIsProposal(currentEditor, rootNode, proposals, node, start);
            addAssignToAssertIsProposal(currentEditor, rootNode, proposals, node, start);
            addPrintProposal(rootNode, proposals, node, start);
            break;
//        case 3100:
//            addShadowReferenceProposal(file, node, rootNode, proposals);
//            break;
//        case 3101:
//        case 3102:
//            addShadowSwitchReferenceProposal(file, node, rootNode, proposals);
//            break;
//        case 5001:
//        case 5002:
//            addChangeIdentifierCaseProposal(node, proposals, file);
//            break;
//        case 6000:
//            addFixMultilineStringIndentation(proposals, file, rootNode, node);
//            break;
//        case 7000:
//            addModuleImportProposals(proposals, project, tc, node);
//            break;
        case 8000:
//            addRenameDescriptorProposal(rootNode, context, problem, proposals, file);
            if (sourceViewer!=null) {
                addMoveDirProposal(file, rootNode, project, proposals, 
                        context);
            }
            break;
//        case 9000:
//            addChangeRefiningTypeProposal(file, rootNode, proposals, node);
//            break;
//        case 9100:
//        case 9200:
//            addChangeRefiningParametersProposal(file, rootNode, proposals, node);
//            break;
//        case 10000:
//            addElseProposal(file, rootNode, proposals, node);
//            addCasesProposal(file, rootNode, proposals, node);
//            break;
//        case 11000:
//            addNamedArgumentsProposal(file, rootNode, proposals, node);
//            break;
//        case 12000:
//        case 12100:
//        	changeToVoid(file, rootNode, node, proposals);
//        	break;
//        case 13000:
//        	changeToFunction(file, rootNode, node, proposals);
//        	break;
//        case 20000:
//            addMakeNativeProposal(proposals, project, node, rootNode, file);
//            break;
        }
    }


    private void addProposals(
            IQuickAssistInvocationContext context,
            CeylonEditor editor,
            Collection<ICompletionProposal> proposals) {
        if (editor==null) return;
        
        IDocument doc = context.getSourceViewer().getDocument();
        IEditorInput input = editor.getEditorInput();
        IProject project = EditorUtil.getProject(input);
        IFile file = EditorUtil.getFile(input);
        IRegion selection = editor.getSelection();
        CeylonParseController parseController =
                editor.getParseController();
        Tree.CompilationUnit rootNode = 
                parseController.getTypecheckedRootNode();
        if (rootNode!=null) {
            int start = context.getOffset();
            int len = context.getLength();
            int end = start + (len>0?len:0); //len==-1 means missing info
            Node node =
                    findNode(rootNode,
                            parseController.getTokens(),
                            start, end);
            int currentOffset = selection.getOffset();
            
            CeylonEditor currentEditor = getCurrentCeylonEditor();

//            addAssignToLocalProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addDestructureProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addAssignToForProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addAssignToIfExistsProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addAssignToAssertExistsProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addAssignToIfNonemptyProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addAssignToAssertNonemptyProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addAssignToTryProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addAssignToIfIsProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addAssignToAssertIsProposal(currentEditor, rootNode, proposals, node, currentOffset);
            addPrintProposal(rootNode, proposals, node, currentOffset);
            
//            addConvertToNamedArgumentsProposal(proposals, file, rootNode, 
//                    editor, currentOffset);
//            addConvertToPositionalArgumentsProposal(proposals, file, rootNode, 
//                    editor, currentOffset);
            
            Tree.Statement statement = findStatement(rootNode, node);
            Tree.Declaration declaration = findDeclaration(rootNode, node);
            Tree.NamedArgument argument = findArgument(rootNode, node);
            Tree.ImportMemberOrType imp = findImport(rootNode, node);
            Tree.OperatorExpression oe = findOperator(rootNode, node);
            
            correctJ2C().addQuickAssists(rootNode, node, project, proposals, 
                    editor, file, doc, statement, declaration, argument,
                    imp, oe, currentOffset);

//            addOperatorProposals(proposals, file, oe);
//            addParenthesesProposals(proposals, file, node, rootNode, oe);

//            addVerboseRefinementProposal(proposals, file, statement, rootNode);
            
//            addAnnotationProposals(proposals, project, declaration,
//                    doc, currentOffset);
//            addTypingProposals(proposals, file, rootNode, node, declaration, editor);
            
//            addAnonymousFunctionProposals(editor, proposals, doc, file, rootNode, 
//                    currentOffset);
            
//            addDeclarationProposals(editor, proposals, doc, file, rootNode, 
//                    declaration, currentOffset);
            
//            addAssignToFieldProposal(file, statement, declaration, proposals);

//            addChangeToIfProposal(proposals, doc, file, rootNode, statement);
            
//            addConvertToDefaultConstructorProposal(proposals, doc, file, rootNode, statement);
            
//            addConvertToClassProposal(proposals, declaration, editor);
//            addAssertExistsDeclarationProposals(proposals, doc, file, rootNode, declaration);
//            addSplitDeclarationProposals(proposals, doc, file, rootNode, declaration, statement);
//            addJoinDeclarationProposal(proposals, rootNode, statement, file);
//            addParameterProposals(proposals, file, rootNode, declaration);
            
//            addArgumentProposals(proposals, doc, file, argument);
            addUseAliasProposal(imp, proposals, editor);
            addRenameAliasProposal(imp, proposals, editor);
            addRemoveAliasProposal(imp, proposals, file, editor);            
            addRenameVersionProposals(node, proposals, rootNode, editor);
            
//            addConvertToIfElseProposal(doc, proposals, file, statement);
//            addConvertToThenElseProposal(rootNode, doc, proposals, file, statement);
//            addInvertIfElseProposal(doc, proposals, file, statement, node, rootNode);
            
//            addConvertSwitchToIfProposal(proposals, doc, file, statement);
//            addConvertIfToSwitchProposal(proposals, doc, file, statement);
            
//            addSplitIfStatementProposal(proposals, doc, file, statement);
//            addJoinIfStatementsProposal(proposals, doc, file, statement);
            
            addConvertGetterToFunctionProposal(proposals, editor, statement);
            addConvertFunctionToGetterProposal(proposals, editor, statement);
            
//            addThrowsAnnotationProposal(proposals, statement, rootNode, file, doc);            

//            addRefineFormalMembersProposal(proposals, node, rootNode, false);
//            addRefineEqualsHashProposal(proposals, node, rootNode);
            
//            addConvertToVerbatimProposal(proposals, file, rootNode, node, doc);
//            addConvertFromVerbatimProposal(proposals, file, rootNode, node, doc);
//            addConvertToConcatenationProposal(proposals, file, rootNode, node, doc);
//            addConvertToInterpolationProposal(proposals, file, rootNode, node, doc);
            
//            addExpandTypeProposal(editor, statement, file, doc, proposals);

            RenameProposal.add(proposals, editor);
            InlineDeclarationProposal.add(proposals, editor);
            ChangeParametersProposal.add(proposals, editor);
            ExtractValueProposal.add(proposals, editor, node);
            ExtractFunctionProposal.add(proposals, editor, node);
            ExtractParameterProposal.add(proposals, editor, node);
            CollectParametersProposal.add(proposals, editor);
            MoveOutProposal.add(proposals, editor, node);
            MakeReceiverProposal.add(proposals, editor, node);
            InvertBooleanProposal.add(proposals, editor);

            MoveToNewUnitProposal.add(proposals, editor);
            MoveToUnitProposal.add(proposals, editor);

        }
        
    }
    
    public void collectWarningProposals(
            CeylonAnnotation annotation,
            IQuickAssistInvocationContext context,
            ProblemLocation location, 
            Collection<ICompletionProposal> proposals) {
        if (annotation.getSeverity()==IMarker.SEVERITY_WARNING) {
            Tree.CompilationUnit rootNode = getRootNode();
            if (rootNode == null) {
                return;
            }
            Node node = findNode(rootNode, null,
                    location.getOffset(),
                    location.getOffset() +
                    location.getLength());

            IEditorInput ei = editor.getEditorInput();
            IFile file = EditorUtil.getFile(ei);
            IDocument doc =
                    context.getSourceViewer()
                        .getDocument();

        	if (annotation.getError() instanceof UsageWarning) {
                correctJ2C().addWarningFixes(location, (UsageWarning) annotation.getError(),
                		getRootNode(), node, 
                		file.getProject(), proposals,
                        getCurrentCeylonEditor(), file,
                        doc);
        	}

            proposals.add(new ConfigureWarningsProposal(editor));
        }

    }
}

//private void addArgumentProposals(
//Collection<ICompletionProposal> proposals,
//IDocument doc, IFile file,
//Tree.StatementOrArgument node) {
//if (node instanceof Tree.MethodArgument) {
//Tree.MethodArgument ma =
//        (Tree.MethodArgument) node;
//Tree.SpecifierOrInitializerExpression se = 
//        ma.getSpecifierExpression(); 
//if (se instanceof Tree.LazySpecifierExpression) {
//    addConvertToBlockProposal(doc, proposals, file, node);
//}
//Tree.Block b = ma.getBlock(); 
//if (b!=null) {
//    addConvertToSpecifierProposal(doc, proposals, file, b);
//}
//}
//if (node instanceof Tree.AttributeArgument) {
//Tree.AttributeArgument aa =
//        (Tree.AttributeArgument) node;
//Tree.SpecifierOrInitializerExpression se = 
//        aa.getSpecifierExpression(); 
//if (se instanceof Tree.LazySpecifierExpression) {
//    addConvertToBlockProposal(doc, proposals, file, node);
//}
//Tree.Block b = aa.getBlock(); 
//if (b!=null) {
//    addConvertToSpecifierProposal(doc, proposals, file, b);
//}
//}
//if (node instanceof Tree.SpecifiedArgument) {
//Tree.SpecifiedArgument sa =
//        (Tree.SpecifiedArgument) node;
//addFillInArgumentNameProposal(proposals, doc, file, sa);
//}
//}

//private void addAnnotationProposals(
//Collection<ICompletionProposal> proposals,
//IProject project, Tree.Declaration decNode,
//IDocument doc, int offset) {
//if (decNode!=null) {
//try {
//  Node in = getIdentifyingNode(decNode);
//  if (in==null ||
//          doc.getLineOfOffset(in.getStartIndex())!=
//                  doc.getLineOfOffset(offset)) {
//      return;
//  }
//}
//catch (BadLocationException e) {
//  e.printStackTrace();
//}
//Declaration d = decNode.getDeclarationModel();
//if (d!=null) {
//  if (decNode instanceof Tree.AttributeDeclaration) {
//      addMakeVariableDecProposal(proposals, project, decNode);
//  }
//  if ((d.isClassOrInterfaceMember()||d.isToplevel()) && 
//          !d.isShared()) {
//      addMakeSharedDecProposal(proposals, project, decNode);
//  }
//  if (d.isClassOrInterfaceMember() &&
//          !d.isDefault() && !d.isFormal()) {
//      if (decNode instanceof Tree.AnyClass) {
//          addMakeDefaultDecProposal(proposals, project, decNode);
//      }
//      else if (decNode instanceof Tree.AnyAttribute) {
//          addMakeDefaultDecProposal(proposals, project, decNode);
//      }
//      else if (decNode instanceof Tree.AnyMethod) {
//          addMakeDefaultDecProposal(proposals, project, decNode);
//      }
//      if (decNode instanceof Tree.ClassDefinition) {
//          addMakeFormalDecProposal(proposals, project, decNode);
//      }
//      else if (decNode instanceof Tree.AttributeDeclaration) {
//          AttributeDeclaration ad = (Tree.AttributeDeclaration) decNode;
//          if (ad.getSpecifierOrInitializerExpression()==null) {
//              addMakeFormalDecProposal(proposals, project, decNode);
//          }
//      }
//      else if (decNode instanceof Tree.MethodDeclaration) {
//          MethodDeclaration md = (Tree.MethodDeclaration) decNode;
//          if (md.getSpecifierExpression()==null) {
//              addMakeFormalDecProposal(proposals, project, decNode);
//          }
//      }
//  }
//}
//}
//}

//@Deprecated
//// see ChangeToQuickFix
//private void changeToFunction(IFile file, 
//    Tree.CompilationUnit rootNode, Node node, 
//    Collection<ICompletionProposal> proposals) {
//Tree.Declaration dec = 
//        findDeclarationWithBody(rootNode, node);
//if (dec instanceof Tree.AnyMethod) {
//    Tree.Return ret = (Tree.Return) node;
//    Tree.AnyMethod m = (Tree.AnyMethod) dec;
//    Tree.Type type = m.getType();
//    if (type instanceof Tree.VoidModifier) {
//        TextFileChange tfc = 
//                new TextFileChange("Change To Function", 
//                        file);
//        Unit unit = rootNode.getUnit();
//        Type rt = 
//                ret.getExpression()
//                    .getTypeModel();
//        tfc.setEdit(new ReplaceEdit(
//                type.getStartIndex(), 
//                type.getDistance(), 
//                isTypeUnknown(rt) ? "function" :
//                    rt.asSourceCodeString(unit)));
//        proposals.add(new CorrectionProposal(
//                "make function non-'void'", tfc, null));
//    }
//}
//}

//@Deprecated
//// see ChangeToQuickFix
//private void changeToVoid(IFile file, 
//    Tree.CompilationUnit rootNode, Node node, 
//    Collection<ICompletionProposal> proposals) {
//Tree.Declaration dec = 
//        findDeclarationWithBody(rootNode, node);
//if (dec instanceof Tree.AnyMethod) {
//    Tree.AnyMethod m = (Tree.AnyMethod) dec;
//    Tree.Type type = m.getType();
//    if (!(type instanceof Tree.VoidModifier)) {
//        TextFileChange tfc = 
//                new TextFileChange("Change To Void", 
//                        file);
//        tfc.setEdit(new ReplaceEdit(
//                type.getStartIndex(), 
//                type.getDistance(), 
//                "void"));
//        proposals.add(new CorrectionProposal(
//                "make function 'void'", tfc, null));
//    }
//}
//}

//@Deprecated
//// See AddNamedArgumentQuickFix
//private void addNamedArgumentsProposal(IFile file,
//      Tree.CompilationUnit rootNode,
//      Collection<ICompletionProposal> proposals, 
//      Node node) {
//  if (node instanceof Tree.NamedArgumentList) {
//      TextFileChange tfc = 
//              new TextFileChange("Add Named Arguments", 
//                      file);
//      IDocument doc = EditorUtil.getDocument(tfc);
//      tfc.setEdit(new MultiTextEdit());
//      Tree.NamedArgumentList nal =
//              (Tree.NamedArgumentList) node;
//      NamedArgumentList args = 
//              nal.getNamedArgumentList();
//      int start = nal.getStartIndex();
//      int stop = nal.getEndIndex()-1;
//      int loc = start+1;
//      String sep = " ";
//      List<Tree.NamedArgument> nas = 
//              nal.getNamedArguments();
//      if (!nas.isEmpty()) {
//          Tree.NamedArgument last = 
//                  nas.get(nas.size()-1);
//          loc = last.getEndIndex();
//          try {
//              int firstLine = 
//                      doc.getLineOfOffset(start);
//              int lastLine = 
//                      doc.getLineOfOffset(stop);
//              if (firstLine!=lastLine) {
//                  sep = utilJ2C().indents().getDefaultLineDelimiter(doc) +
//                          utilJ2C().indents().getIndent(last, doc);
//              }
//          }
//          catch (BadLocationException e) {
//              e.printStackTrace();
//          }
//      }
//      ParameterList params = args.getParameterList();
//      String result = null;
//      boolean multipleResults = false;
//      for (Parameter param: params.getParameters()) {
//          if (!param.isDefaulted() &&
//              !args.getArgumentNames()
//                  .contains(param.getName())) {
//              multipleResults = result!=null;
//              result = param.getName();
//              tfc.addEdit(new InsertEdit(loc, 
//                      sep + param.getName() + 
//                      " = nothing;"));
//          }
//      }
//      if (loc==stop) {
//          tfc.addEdit(new InsertEdit(stop, " "));
//      }
//      String name = multipleResults ?
//          "Fill in missing named arguments" :
//          "Fill in missing named argument '" 
//              + result + "'";
//      proposals.add(new CorrectionProposal(name, tfc, 
//              new Region(loc, 0)));
//  }
//}

//@Deprecated
//// see SwitchQuickFix
//private void addElseProposal(IFile file, 
//      Tree.CompilationUnit rootNode,
//      Collection<ICompletionProposal> proposals, 
//      Node node) {
//  if (node instanceof Tree.SwitchClause) {
//      Tree.Statement st = 
//              findStatement(rootNode, node);
//      if (st instanceof Tree.SwitchStatement) {
//          int offset = st.getEndIndex();
//          TextFileChange tfc = 
//                  new TextFileChange("Add Else", file);
//          IDocument doc = getDocument(tfc);
//          String text = 
//                  utilJ2C().indents().getDefaultLineDelimiter(doc) +
//                  utilJ2C().indents().getIndent(node, doc) +
//                  "else {}";
//          tfc.setEdit(new InsertEdit(offset, text));
//          Region selection =
//                  new Region(offset+text.length()-1, 0);
//          proposals.add(new CorrectionProposal(
//                  "Add 'else' clause", 
//                  tfc, selection));
//      }
//      //TODO: else handle switch *expressions* 
//  }
//}

//@Deprecated
//// see SwitchQuickFix
//private void addCasesProposal(IFile file, 
//      Tree.CompilationUnit rootNode,
//      Collection<ICompletionProposal> proposals, 
//      Node node) {
//  if (node instanceof Tree.SwitchClause) {
//      Tree.SwitchClause sc = (Tree.SwitchClause) node;
//      Tree.Statement st = 
//              findStatement(rootNode, node);
//      if (st instanceof Tree.SwitchStatement) {
//          //TODO: handle switch expressions!
//          Tree.SwitchStatement ss = 
//                  (Tree.SwitchStatement) st;
//          Tree.Expression e = 
//                  sc.getSwitched()
//                      .getExpression();
//          if (e!=null) {
//              Type type = e.getTypeModel();
//              if (type!=null) {
//                  Tree.SwitchCaseList scl = 
//                          ss.getSwitchCaseList();
//                  for (Tree.CaseClause cc: 
//                          scl.getCaseClauses()) {
//                      Tree.CaseItem item = 
//                              cc.getCaseItem();
//                      if (item instanceof Tree.IsCase) {
//                          Tree.IsCase ic = 
//                                  (Tree.IsCase) item;
//                          Tree.Type tn = ic.getType();
//                          if (tn!=null) {
//                              Type t = 
//                                      tn.getTypeModel();
//                              if (!isTypeUnknown(t)) {
//                                  type = type.minus(t);
//                              }
//                          }
//                      }
//                      else if (item instanceof Tree.MatchCase) {
//                          Tree.MatchCase ic = 
//                                  (Tree.MatchCase) item;
//                          Tree.ExpressionList il = 
//                                  ic.getExpressionList();
//                          for (Tree.Expression ex: 
//                              il.getExpressions()) {
//                              if (ex!=null) {
//                                  Type t = ex.getTypeModel();
//                                  if (t!=null && 
//                                          !isTypeUnknown(t)) {
//                                      type = type.minus(t);
//                                  }
//                              }
//                          }
//                      }
//                  }
//                  TextFileChange tfc = 
//                          new TextFileChange(
//                                  "Add Cases", file);
//                  IDocument doc = getDocument(tfc);
//                  String text = "";
//                  List<Type> list;
//                  List<Type> cts = type.getCaseTypes();
//                  if (cts!=null) {
//                      list = cts;
//                  }
//                  else {
//                      list = singletonList(type);
//                  }
//                  for (Type pt: list) {
//                      String is = 
//                              pt.getDeclaration()
//                                  .isAnonymous() ? 
//                              "" : "is ";
//                      Unit unit = rootNode.getUnit();
//                  text += utilJ2C().indents().getDefaultLineDelimiter(doc) +
//                          utilJ2C().indents().getIndent(node, doc) +
//                              "case (" +
//                              is + 
//                              pt.asString(unit) +
//                              ") {}"; 
//                  }
//                  int offset = ss.getEndIndex();
//                  tfc.setEdit(new InsertEdit(offset, text));
//                  proposals.add(new CorrectionProposal(
//                          "Add missing 'case' clauses", tfc, 
//                          new Region(offset+text.length()-1, 0)));
//              }
//          }
//      }
//  }        
//}

//@Deprecated
//// see AddTypeParameterQuickFix
//void addTypeParameterProposal(IFile file, 
//      Tree.CompilationUnit rootNode,
//      Collection<ICompletionProposal> proposals, 
//      Node node) {
//  Tree.TypeConstraint tcn = (Tree.TypeConstraint) node;
//  TypeParameter tp = tcn.getDeclarationModel();
//  Tree.Declaration decNode = 
//          (Tree.Declaration) 
//              getReferencedNodeInUnit(
//                      tp.getDeclaration(), 
//                      rootNode);
//  Tree.TypeParameterList tpl;
//  if (decNode instanceof Tree.ClassOrInterface) {
//      Tree.ClassOrInterface ci = 
//              (Tree.ClassOrInterface) decNode;
//      tpl = ci.getTypeParameterList();
//  }
//  else if (decNode instanceof Tree.AnyMethod) {
//      Tree.AnyMethod am = (Tree.AnyMethod) decNode;
//      tpl = am.getTypeParameterList();
//  }
//  else if (decNode instanceof Tree.TypeAliasDeclaration) {
//      Tree.TypeAliasDeclaration ad = 
//              (Tree.TypeAliasDeclaration) decNode;
//      tpl = ad.getTypeParameterList();
//  }
//  else {
//      return;
//  }
//  TextFileChange tfc = 
//          new TextFileChange("Add Type Parameter", file);
//  InsertEdit edit;
//  if (tpl==null) {
//      Tree.Identifier id = decNode.getIdentifier();
//      edit = new InsertEdit(id.getEndIndex(),
//              "<" + tp.getName() + ">");
//  }
//  else {
//      edit = new InsertEdit(tpl.getEndIndex()-1,
//              ", " + tp.getName());
//  }
//  tfc.setEdit(edit);
//  proposals.add(new CorrectionProposal(
//          "Add '" + tp.getName() +
//          "' to type parameter list of '" + 
//          decNode.getDeclarationModel().getName() + "'", 
//          tfc, null));
//}

//@Deprecated
//private static void addOperatorProposals(
//      Collection<ICompletionProposal> proposals,
//      IFile file,
//      Tree.OperatorExpression oe) {
//  if (oe instanceof Tree.BinaryOperatorExpression) {
//      Tree.BinaryOperatorExpression boe =
//              (Tree.BinaryOperatorExpression) oe;
//      addReverseOperatorProposal(proposals, file, boe);
//      addInvertOperatorProposal(proposals, file, boe);
//      addSwapBinaryOperandsProposal(proposals, file, boe);
//  }
//}


//@Deprecated
//private static void addAnonymousFunctionProposals(
//        CeylonEditor editor,
//        Collection<ICompletionProposal> proposals,
//        IDocument doc, IFile file,
//        Tree.CompilationUnit rootNode,
//        final int currentOffset) {
//    class FindAnonFunctionVisitor extends Visitor {
//        Tree.FunctionArgument result;
//        public void visit(Tree.FunctionArgument that) {
//            if (currentOffset>=that.getStartIndex() &&
//                currentOffset<=that.getEndIndex()) {
//                result = that;
//            }
//            super.visit(that);
//        }
//    }
//    FindAnonFunctionVisitor v = new FindAnonFunctionVisitor();
//    v.visit(rootNode);
//    Tree.FunctionArgument fun = v.result;
//    if (fun!=null) {
//        if (fun.getExpression()!=null) {
//            addConvertToBlockProposal(doc, proposals, file, fun);
//        }
//        if (fun.getBlock()!=null) {
//            addConvertToSpecifierProposal(doc, proposals, file, 
//                    fun.getBlock(), true);
//        }
//    }
//}

//@Deprecated
//private static void addDeclarationProposals(
//        CeylonEditor editor,
//        Collection<ICompletionProposal> proposals,
//        IDocument doc, IFile file,
//        Tree.CompilationUnit rootNode,
//        Tree.Declaration decNode,
//        int currentOffset) {
//    
//    if (decNode==null) return;
//    
//    if (decNode.getAnnotationList()!=null) {
//        Integer endIndex =
//                decNode.getAnnotationList().getEndIndex();
//        if (endIndex!=null && currentOffset<=endIndex) {
//            return;
//        }
//    }
//    if (decNode instanceof Tree.TypedDeclaration) {
//        Tree.TypedDeclaration tdn = 
//                (Tree.TypedDeclaration) decNode;
//        if (tdn.getType()!=null) {
//            Integer endIndex = tdn.getType().getEndIndex();
//            if (endIndex!=null && currentOffset<=endIndex) {
//                return;
//            }
//        }
//    }
//        
//    if (decNode instanceof Tree.AttributeDeclaration) {
//        Tree.AttributeDeclaration attDecNode = 
//                (Tree.AttributeDeclaration) decNode;
//        Tree.SpecifierOrInitializerExpression se = 
//                attDecNode.getSpecifierOrInitializerExpression(); 
//        if (se instanceof Tree.LazySpecifierExpression) {
//            addConvertToBlockProposal(doc, proposals, file, decNode);
//        }
//        else {
//            addConvertToGetterProposal(doc, proposals, file, attDecNode);
//        }
//    }
//    if (decNode instanceof Tree.MethodDeclaration) {
//        Tree.MethodDeclaration methodDecNode = 
//                (Tree.MethodDeclaration) decNode;
//        Tree.SpecifierOrInitializerExpression se = 
//                methodDecNode.getSpecifierExpression(); 
//        if (se instanceof Tree.LazySpecifierExpression) {
//            addConvertToBlockProposal(doc, proposals, file, decNode);
//        }
//    }
//    if (decNode instanceof Tree.AttributeSetterDefinition) {
//        Tree.AttributeSetterDefinition setterDefNode = 
//                (Tree.AttributeSetterDefinition) decNode;
//        Tree.SpecifierOrInitializerExpression se = 
//                setterDefNode.getSpecifierExpression();
//        if (se instanceof Tree.LazySpecifierExpression) {
//            addConvertToBlockProposal(doc, proposals, file, decNode);
//        }
//        Tree.Block b = setterDefNode.getBlock(); 
//        if (b!=null) {
//            addConvertToSpecifierProposal(doc, proposals, file, b);
//        }
//    }
//    if (decNode instanceof Tree.AttributeGetterDefinition) {
//        Tree.AttributeGetterDefinition getterDefNode = 
//                (Tree.AttributeGetterDefinition) decNode;
//        Tree.Block b = getterDefNode.getBlock(); 
//        if (b!=null) {
//            addConvertToSpecifierProposal(doc, proposals, file, b);
//        }
//    }
//    if (decNode instanceof Tree.MethodDefinition) {
//        Tree.MethodDefinition methodDefNode = 
//                (Tree.MethodDefinition) decNode;
//        Tree.Block b = methodDefNode.getBlock(); 
//        if (b!=null) {
//            addConvertToSpecifierProposal(doc, proposals, file, b);
//        }
//    }
//    
//}

//@Deprecated
//private void addCreationProposals(
//      Tree.CompilationUnit cu, 
//      final Node node, 
//      ProblemLocation problem, 
//      Collection<ICompletionProposal> proposals, 
//      IProject project,
//      IFile file) {
//  if (node instanceof Tree.MemberOrTypeExpression) {
//      addCreateProposals(cu, node, proposals, project, file);
//  }
//  else if (node instanceof Tree.SimpleType) {
//      class FindExtendedTypeExpressionVisitor extends Visitor {
//          Tree.InvocationExpression invocationExpression;
//          @Override
//          public void visit(Tree.ExtendedType that) {
//              super.visit(that);
//              if (that.getType()==node) {
//                  invocationExpression = 
//                          that.getInvocationExpression();
//              }
//          }
//      }
//      FindExtendedTypeExpressionVisitor v = 
//              new FindExtendedTypeExpressionVisitor();
//      v.visit(cu);
//      if (v.invocationExpression!=null) {
//          addCreateProposals(cu, 
//                  v.invocationExpression.getPrimary(), 
//                  proposals, project, file);
//      }
//  }
//  //TODO: should we add this stuff back in??
//  /*else if (node instanceof Tree.BaseType) {
//      Tree.BaseType bt = (Tree.BaseType) node;
//      String brokenName = bt.getIdentifier().getText();
//      String idef = "interface " + brokenName + " {}";
//      String idesc = "interface '" + brokenName + "'";
//      String cdef = "class " + brokenName + "() {}";
//      String cdesc = "class '" + brokenName + "()'";
//      //addCreateLocalProposals(proposals, project, idef, idesc, INTERFACE, cu, bt);
//      addCreateLocalProposals(proposals, project, cdef, cdesc, CLASS, cu, bt, null, null);
//      addCreateToplevelProposals(proposals, project, idef, idesc, INTERFACE, cu, bt, null, null);
//      addCreateToplevelProposals(proposals, project, cdef, cdesc, CLASS, cu, bt, null, null);
//      CreateInNewUnitProposal.addCreateToplevelProposal(proposals, idef, idesc, 
//              INTERFACE, file, brokenName, null, null);
//      CreateInNewUnitProposal.addCreateToplevelProposal(proposals, cdef, cdesc, 
//              CLASS, file, brokenName, null, null);
//      
//  }*/
//  if (node instanceof Tree.BaseType) {
//      Tree.BaseType bt = (Tree.BaseType) node;
//      Tree.Identifier id = bt.getIdentifier();
//      if (id!=null) {
//          String brokenName = id.getText();
//          addCreateTypeParameterProposal(proposals,
//                  project, cu, bt, brokenName);
//      }
//  }
//}
